package cn.flant.db;

import java.sql.Connection;
import java.sql.SQLException;

import cn.flant.db.exec.BasicExecutor;
import cn.flant.db.expt.DbException;
import cn.flant.db.hdle.ExceptionHandler;
import cn.flant.db.mgr.ConnectionManager;

public final class Transaction extends BasicExecutor {
	private DbUtil dbUtil;
	private ConnectionManager mgr = null;
	private Connection connection;
	private boolean throwException;

	protected Transaction(DbUtil dbUtil) {
		super(dbUtil.getTimeout());
		this.dbUtil = dbUtil;
		this.mgr = dbUtil.getConnectionManager();
		setDialect(dbUtil.getDialect());
		setShowExecuteInfo(dbUtil.isShowExecuteInfo());
		setThrowException(true);
	}

	public boolean isThrowException() {
		return throwException;
	}

	public void setThrowException(boolean throwException) {
		this.throwException = throwException;
		if (throwException) {
			setExceptionHandler(new ThrowableExceptionHandler());
		} else {
			setExceptionHandler(dbUtil.getExceptionHandler());
		}
	}

	public synchronized boolean begin() {
		boolean result = false;
		try {
			if (connection == null) {
				connection = mgr.getConnection();
				if (connection == null) {
					throw new Exception("Cant get connection");
				}
			}
			if (connection.getAutoCommit()) {
				connection.setAutoCommit(false);
			}
			result = true;
		} catch (Exception e) {
			if (connection != null) {
				mgr.closeConnection(connection);
				connection = null;
			}
			if (exceptionHandler != null) {
				exceptionHandler.handleException(e);
			} else {
				log.error("Begin transaction failed: " + e.getMessage(), e);
			}
		}
		return result;
	}

	public synchronized boolean rollback() {
		boolean result = false;
		try {
			if (connection == null) {
				throw new Exception("Connection is closed");
			}
			connection.rollback();
			result = true;
		} catch (Exception e) {
			if (exceptionHandler != null) {
				exceptionHandler.handleException(e);
			} else {
				log.error("Rollback transaction failed: " + e.getMessage(), e);
			}
		}
		return result;
	}

	public synchronized boolean commit() {
		boolean result = false;
		try {
			if (connection == null) {
				throw new Exception("Connection is closed");
			}
			connection.commit();
			result = true;
		} catch (Exception e) {
			if (exceptionHandler != null) {
				exceptionHandler.handleException(e);
			} else {
				log.error("Commit transaction failed: " + e.getMessage(), e);
			}
		}
		return result;
	}

	public synchronized void end() {
		if (connection != null) {
			try {
				connection.setAutoCommit(true);
			} catch (Exception e) {
			}
			mgr.closeConnection(connection);
			connection = null;
		}
	}

	@Override
	protected Connection getConnection() throws SQLException {
		if (connection == null) {
			throw new SQLException("Transaction has not yet started");
		}
		return connection;
	}

	@Override
	protected void closeConnection(Connection conn) {
		return;
	}

	private class ThrowableExceptionHandler implements ExceptionHandler {
		public void handleException(Throwable e) {
			throw new DbException(e.getMessage(), e);
		}
	}
}
